This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Patch: Problem with type safety and the "sentinel" attribute


 > That is: either we use something like
 > 
 >   __attribute__ ((sentinel ([position [ , named? [ , type [ , value ]]]])));
 > 
 > where
 > 
 >   position is the position from the end of the argument list
 >   named?   is 0 for the old behaviour and 1 for the behaviour I
 >            implemented

Ok I guess extending the original attribute this way is backwards
compatible since no one has yet implemented arguments beyond the
position parameter.

I'm wondering whether "named?" should be a two-value parameter as you
suggest or another position parameter.  E.g. consider this prototype:

	void foo1(const char *arg1, const char *arg2, const char *arg3, ...);

Is it always the case that a sentinel optionally appearing in the
named arguments appears in the *last* named argument, arg3?  If not,
then let's say that the sentinel can appear in arg2, does that mean
arg2 or later (i.e. arg3 also?) or only arg2?

If we say arg2 or later then what about cases where the later
arguments don't have matching types?  E.g. this prototype:

	void foo1(const char *arg1, int arg2, int arg3, ...);

Here only arg1 can have the sentinel from a type standpoint.

To avoid confusion, I'd say that we should only allow exactly one
named argument to optionally contain the sentinel.  But the user can
specify which one it will be.  Of course, we should ensure that the
type of the chosen argument matches that of the sentinel.  Or for now
that it is at least a pointer.

Another issue is what does it mean to have a non-zero position
parameter when the sentinel appears in the named arguments?  For
consistency, I believe if "position" is say 5, then we should still
have 5 variable arguments even if the sentinel appears in one of the
named arguments.


 >   type     is one of the three types that Tim suggested (see other mail)
 >   value    is NULL/0 by default but can be set to -1 or something else
 >            when necessary

I had originally envisioned just a value parameter rather than
separate type and value.  E.g.: plain "0" is int, whereas "0L" is
long.  You could use casts for other types like "(size_t)0" or
"(__PTRDIFF_TYPE__)0" to use gcc's builtin types rather than ptrdiff_t
which might not be declared early enough.  This part is kind of moot
since neither of us is proposing to implement the changable value
stuff.  However I wanted to clarify my syntax preferece. :-)


So IMHO, we should define the syntax as this:

__attribute__ ((sentinel [, variable_arguments_after_sentinel [, named_argument_containing_sentinel [, sentinel_value]]]))

The square-brackets denote optional args whose default values are:
	variable_arguments_after_sentinel=0
	named_argument_containing_sentinel=0
	sentinel_value=(void *)0

Note calling the first parameter "variable_arguments_after_sentinel"
is just a name change and it's backwards compatible with the replaced
"sentinel_position_from_end".  I just think it conveys the meaning
better.

The new parameter "named_argument_containing_sentinel" specifies which
named argument optionally may contain the sentinel.  Position 0 for
this means the sentinel has no effect appearing in the named
arguments, which is the current behavior.  (I'm not sure whether a
non-zero position should count from the start or end of the named
parameter list.  If we count backwards, then a 1 means the same as
your boolean example and follows the lead of the old
sentinel_position_from_end parameter.)

So assuming we count backwards among the named arguments, then
e.g. given this prototype:

	void foo1(const char *arg1, const char *arg2, const char *arg3, ...) __attribute__ ((sentinel, 0, 0));

this would be the same as the existing default:

	void foo1(const char *arg1, const char *arg2, const char *arg3, ...) __attribute__ ((sentinel));

In another example:

	void foo1(const char *arg1, const char *arg2, const char *arg3, ...) __attribute__ ((sentinel, 0, 1));

this would be what I understand you want which is to allow optionally
to have the sentinel only in arg3 and no variable arguments when that
happens, otherwise the sentinel must appear as the last variable
argument.  AKA this function is "null terminated".

A more complicated example would be:

        void foo1(const char *arg1, const char *arg2, const char *arg3, ...) __attribute__ ((sentinel, 4, 3));

This one means that arg1 may contain the sentinel (because it's third
counting from the end of the named list), in which case exactly 4
variable arguments appear.  Otherwise the sentinel appears anywhere in
the variable arguments and exactly 4 arguments must appear after it,
as is the case with the current sentinel attribute behavior.

I believe this provides for flexibility to do complicated stuff while
also allowing for your needs to be met.

Thoughts?

		--Kaveh
--
Kaveh R. Ghazi			ghazi@caip.rutgers.edu


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]